use core::fmt;
use std::fs;
use std::path::PathBuf;
use std::process::{Command, Output};

#[derive(Debug)]
pub struct ExecutionResult {
    pub stdout: String, // 标准输出
    pub stderr: String, // 标准错误
    pub exit_code: i32, // 退出码
}

impl fmt::Display for ExecutionResult {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        writeln!(
            f,
            "success msg: {}, error msg: {}, exit_code: {}",
            self.stdout, self.stderr, self.exit_code
        )
    }
}

impl ExecutionResult {
    pub fn is_single_line_and_not_empty(&self) -> bool {
        let trimmed_out = self.stdout.trim();
        !trimmed_out.is_empty() && !trimmed_out.contains("\n") && trimmed_out != "[Empty]"
    }
}

pub enum OsType {
    Windows,
    Linux,
    Mac,
    Unknown,
}

pub fn get_os_type() -> OsType {
    match std::env::consts::OS {
        "windows" => return OsType::Windows,
        "mac" => return OsType::Mac,
        "linux" => return OsType::Linux,
        _ => return OsType::Unknown,
    }
}

pub fn get_tool_path(tool_name: &str) -> Result<PathBuf, Box<dyn std::error::Error>> {
    let current_exe_path = std::env::current_exe()?;
    let current_dir = current_exe_path
        .parent()
        .ok_or("Failed to get parent directory")?;
    Ok(current_dir.join(tool_name))
}

pub fn execute_tool(tool_name: &str, args: &[&str]) -> Result<ExecutionResult, Box<dyn std::error::Error>> {
    let tool_path = get_tool_path(tool_name)?;
    let mut command = Command::new(tool_path);
    command.args(args);

    let output: Output = command.output()?;
    let stdout = String::from_utf8_lossy(&output.stdout).into_owned();
    let stderr = String::from_utf8_lossy(&output.stderr).into_owned();
    let exit_code = output.status.code().unwrap_or(-1);
    Ok(ExecutionResult {
        stdout,
        stderr,
        exit_code,
    })
}

pub fn create_device_log_file(device: &str) -> Result<String, Box<dyn std::error::Error>> {
    let current_exe_path = std::env::current_exe()?;
    let current_dir = current_exe_path
        .parent()
        .ok_or("Failed to get parent directory")?;
    let new_log_path = current_dir.join(".log").join(device);
    if new_log_path.exists() && new_log_path.is_dir() {
        let _ = fs::remove_dir_all(&new_log_path);
        println!("Delete old log cache success!");
    }
    let _ = fs::create_dir_all(&new_log_path);
    let path = new_log_path.to_str()
        .ok_or_else(|| std::io::Error::new(std::io::ErrorKind::Other, "contain invalid symbol"))
        .map(|s| s.to_string())?;
    println!("Create log cache path success: {}", path);
    Ok(path)
}